import processing.opengl.*;


import javax.media.opengl.*;
 
GL gl;

int debugCounter = 0;

String getLevelLocation(int levelNumber){
  if (levelNumber == 1){ return "level1.rgb";}
  if (levelNumber == 2){ return "level2.rgb";}
  else{ 
    println("Level number "+levelNumber+" does not exist.  Loading default level.");
    return "testLevel.rgb"; 
//      return "debugLevel.rgb";
  }
}

void playerWonLevel(){
  myPlayer.drawn = false;
  myPlayer.simulating = false;
  for (int i=0; i < myPlayer.particle.length; i++){
    Particle particle = myPlayer.particle[i];
    burstFXParticles(100,particle.position,particle.position.minus(particle.positionLast),particle.myColor,1.1,255.0);
  }
  alreadyWonLevel = true;
  println("You beat the level in "+frameNumber+" frames!");
  currentLevel++;
}
float shootAngle = 0.0;
void goalCheck(){
  //nothing yet...
  
  if (!alreadyWonLevel){
  //add some FX particles to show the goal

  Vector3d position = goalLocation;
//  Vector3d positionL = new Vector3d(goalLocation);
  Vector3d myVec = goalLocation.minus(myPlayer.particle[0].position);
  float normSqr = myVec.normSquared();
  if (normSqr > 3.0){
  shootAngle += .01;//random(-.02,.1);
  Vector3d radiusVec = new Vector3d(25*cos(shootAngle),25*sin(shootAngle),0);
  Vector3d diffVec = new Vector3d(-radiusVec.x/10.0, -radiusVec.y/10.0 - 2.0,0);//myVec.normalized().times( 0 );//min(3.0+10000.0/normSqr,20) );
  int rnum = (int) ( 2.0 + 2*sin(frameNumber / 100.0+shootAngle) );
  int gnum = (int) ( 2.0 + 2*sin(frameNumber / 100.0 + PI*2.0/3.0+shootAngle) );
  int bnum = (int) ( 2.0 + 2*sin(frameNumber / 100.0 + PI*4.0/3.0+shootAngle) );
  burstFXParticles(rnum,position.plus(radiusVec),diffVec,REDVEC,.6*min(3.0+10000.0/normSqr,10.0),215.0);
  burstFXParticles(gnum,position.plus(radiusVec),diffVec,GREENVEC,.6*min(3.0+10000.0/normSqr,10.0),215.0);
  burstFXParticles(bnum,position.plus(radiusVec),diffVec,BLUEVEC,.6*min(3.0+10000.0/normSqr,10.0),215.0);
  
  if (1.0+10000.0/normSqr > 5.0) {
    for (int i=0; i<myPlayer.particle.length; i++){
//      Vector3d myVec2 = goalLocation.minus(myPlayer.particle[i].position);
//      myVec2.multiply(1000.0);
      myPlayer.particle[i].position.x = goalLocation.x*.001 + myPlayer.particle[i].position.x * .999;
      myPlayer.particle[i].position.y = goalLocation.y*.001 + myPlayer.particle[i].position.y * .999; 
    }
  }
  if (1.0+10000.0/normSqr > 20.0) {
    /*for (int i=0; i<myPlayer.particle.length; i++){
      Vector3d myVec2 = goalLocation.minus(myPlayer.particle[i].position).times(1000.0);
      myPlayer.particle[i].position.x = goalLocation.x*.1 + myPlayer.particle[i].position.x * .9;
      myPlayer.particle[i].position.y = goalLocation.y*.1 + myPlayer.particle[i].position.y * .9; 
    }*/
    //player won
    playerWonLevel();
  }
  } else{
    /*for (int i=0; i<myPlayer.particle.length; i++){
      Vector3d myVec2 = goalLocation.minus(myPlayer.particle[i].position).times(1000.0);
      myPlayer.particle[i].position.x = goalLocation.x*.1 + myPlayer.particle[i].position.x * .9;
      myPlayer.particle[i].position.y = goalLocation.y*.1 + myPlayer.particle[i].position.y * .9; 
    }*/
    playerWonLevel();
  }
  }
}

String graphicsMode;
void setup(){
//  size(500,400,P3D); graphicsMode = P3D;
  size(640,480,OPENGL); graphicsMode = OPENGL;
  if (graphicsMode.equals(OPENGL)) {
    smooth();
    gl = ((PGraphicsOpenGL) g).gl;
  }
  frameRate(24);
  currentLevel = 1;
  levelInitialize();
}

void levelInitialize(){
  debugVC = 0;
  //if (rUpCount > 1){ recording = false; playingBack = true; keyState[' '] = false;frameRate(24);bgcolor = 0;}
  recording = true;
  playingBack = false;
  rDownPlay = 0;
  gDownPlay = 0;
  bDownPlay = 0;
  rightDownPlay = 0;
  leftDownPlay = 0;
  rUpPlay = 0;
  gUpPlay = 0;
  bUpPlay = 0;
  rightUpPlay = 0;
  leftUpPlay = 0;
//  currentLevel = 1;
  noStroke();
  keyState['r'] = false;
  keyState['g'] = false;
  keyState['b'] = false;
  generatePoses();
  myPlayer = new Player(myParticle);
//  myPlayer.scale(.91);
  myPlayer.rotate(-PI/2.0);
  myPlayer.translate(new Vector3d(100,100,0)); //default player position
  cameraX = -100+width/2; //to center the camera at CAMX, set cameraX = -CAMX + width/2, cameraY = -CAMY + height/2
  cameraY = -100+height/2;
  alreadyWonLevel = false;
  goalLocation = new Vector3d(550,-65,0); //eh, default
  collidePoint = new Vector3d(10,0,0);
  gravityForce = new Vector3d(0.0,gravityStrength,0);
  //  myStrut = new Strut[4];//(myPlayer.getParticle(0),myPlayer.getParticle(1));
  myStrut = new ArrayList();
  myStaticCStruts = new ArrayList();
  myDynamicCStruts = new ArrayList();
  otherParticle = new ArrayList();
  redDStruts = new ArrayList();
  greenDStruts = new ArrayList();
  blueDStruts = new ArrayList();
  alphaDStruts = new ArrayList();
  myWall = new ArrayList();
  redGrabbing = false;
  greenGrabbing = false;
  blueGrabbing = false;
  alphaGrabbing = false;
  invertedRGB = false;
  keyLeft = false;
  keyRight = false;
  angularVelocity = 0.0;
  smoothedAngularVelocity = 0.0;
  smoothedMouseForce = 0.0;
  frameNumber = 0;
  stepCount = 0;
  //makeWalls();
  //makeStruts();
  //  loadLevelLocation(getParameter("levelFile"));
  /*
  if (levelLocation == null) {
    if (inputLevelString == null){
      try{
        makeLevel("http://www.gamefight.org/rgb2/defaultLevel.rgb");
      } 
      catch (Exception e){
        makeLevel("testLevel.rgb");
      }
    } 
    else{
      makeLevelFromString(inputLevelString);
    }
  } 
  else{
    makeLevel(levelLocation);
  }*/
  String levelToLoad = getLevelLocation(currentLevel);
  try{
    makeLevel(levelToLoad);
  } catch (Exception e){
    makeLevel(getLevelLocation(0)); //will load default level, which better exist!
  }

  //  levelLocation = null;
  //  testImage = loadImage("blurcheck.jpg");
  /*  PImage[] imageArray = new PImage[10];
   imageArray[0] = loadImage("t1.png");
   imageArray[1] = loadImage("t2.png");
   imageArray[2] = loadImage("t3.png");
   imageArray[3] = loadImage("t4.png");
   imageArray[4] = loadImage("t5.png");
   imageArray[5] = loadImage("t6.png");
   imageArray[6] = loadImage("t5.png");
   imageArray[7] = loadImage("t4.png");
   imageArray[8] = loadImage("t3.png");
   imageArray[9] = loadImage("t2.png");
   
   testSprite = new Sprite(imageArray,new Vector3d(200,100,0),100,100,1.0);
   */
  fxParticles = new Particle[numFXParticles];
  for (int i=0; i<numFXParticles; i++){
    fxParticles[i] = new Particle(new Vector3d(ZEROVEC), new Vector3d(ZEROVEC), 1.0, 0.9, new Vector3d(WHITEVEC));
    fxParticles[i].setLineDraw();
  }
  activeFXParticles = 0;

  //Might as well start things off with a burst
  for (int i=0; i < myPlayer.particle.length; i++){
    Particle particle = myPlayer.particle[i];
    burstFXParticles(60,particle.position,particle.position.minus(particle.positionLast),particle.myColor,1.0,455.0);
  }
  setupSpatialPartition();
  System.gc();
}

void setupSpatialPartition(){
  mySpatialPartition = new SpatialPartition(100,myWall,myStaticCStruts,myDynamicCStruts);
}



void draw(){
  //println(frameRate);
  debugCounter = 0;
  debugFlag = keyState['d'];
  if (keyState[' ']) {levelInitialize();}
  background(bgcolor);
  //fill(0,0,0,120); 
  //rect(-1,0,width+1,height+1);
  //handleCamera();
  pushMatrix();
  translate(cameraX,cameraY);
  drawStars();
  popMatrix();

  calcAngularVelocity(); //use this for some transformations  

  //do simulation loop
  for (int i=0; i<stepsPerFrame; i++){
    if (playingBack) loadKeyStates();

    handleCamera();
    clickHandler(); //actually handles rotation in general, not clicks
    keyCheck(); //this is where collision checks with colored struts happen - MUST BE INSIDE MULTIPLE SIMULATOR!!! [bad way to design this, though...]
    goalCheck(); //check if player has won level/picked up anything
    myPlayer.timeStep(tstep/stepsPerFrame);
    mySpatialPartition.refreshDynamicPartitioning();//doesn't need to be done EVERY time step...

    //check collisions - use SpatialPartition to get list of walls in range
    Vector3d playerCenter = new Vector3d(myPlayer.getParticle(0).position);
    SolidWall2d[] collidingWalls = mySpatialPartition.getWallsInRange(playerCenter.x-100,playerCenter.y-100,playerCenter.x+100,playerCenter.y+100);
    for (int j=0; j<myPlayer.numParticles;j++){
      for (int k=0; k<collidingWalls.length; k++){
        collidingWalls[k].collideTest(myPlayer.getParticle(j),collidePoint);
      }
    }
    //    if (debugFlag){ println(collidingWalls.length); }
    for (int j=0; j<myPlayer.numParticles;j++){
      //add gravity
      myPlayer.getParticle(j).forceAdd(gravityForce.times(myPlayer.getParticle(j).mass));
    }
    for (int k=0; k<otherParticle.size();k++){
      ((Particle)otherParticle.get(k)).forceAdd(gravityForce.times( ((Particle)otherParticle.get(k)).mass ));
    }    
    for (int j=0; j<otherParticle.size();j++){
      ((Particle)otherParticle.get(j)).timeStep(tstep/stepsPerFrame);
    }
    for (int k=0; k<otherParticle.size();k++){
      //collide other particles with walls (alter velocities)
      Particle oParticle = (Particle)otherParticle.get(k);
      collidingWalls = mySpatialPartition.getWallsInRange(min(oParticle.position.x,oParticle.truePositionLast.x)-100,
      min(oParticle.position.y,oParticle.truePositionLast.y)-100,
      max(oParticle.position.x,oParticle.truePositionLast.x)+100,
      max(oParticle.position.y,oParticle.truePositionLast.y)+100);
      for (int m=0; m<collidingWalls.length; m++){
//        println("particle "+k);
        collidingWalls[m].collideTest( (Particle)otherParticle.get(k),collidePoint);
      }
    }
    if (i==0){
      for (int k=0; k<activeFXParticles;k++){
        //collide FX particles with walls (alter velocities)
        Particle oParticle = fxParticles[k];//.get(k);
        collidingWalls = mySpatialPartition.getWallsInRange(min(oParticle.position.x,oParticle.truePositionLast.x)-10,
        min(oParticle.position.y,oParticle.truePositionLast.y)-10,
        max(oParticle.position.x,oParticle.truePositionLast.x)+10,
        max(oParticle.position.y,oParticle.truePositionLast.y)+10);
        for (int m=0; m<collidingWalls.length; m++){
          float origRest = collidingWalls[m].restCoef;
          float origFric = collidingWalls[m].fricCoef;
          collidingWalls[m].restCoef = 0.8;
          collidingWalls[m].fricCoef = 0.2;
          collidingWalls[m].collideTest( fxParticles[k],collidePoint);
          collidingWalls[m].restCoef = origRest;
          collidingWalls[m].fricCoef = origFric;
        }
      }

    }
    for (int l = 0; l<constraintIters; l++){
      for (int j=0; j<myStrut.size();j++){
        ((Strut)myStrut.get(j)).applyConstraint();
      }      
      for (int j=0; j<redDStruts.size();j++){
        ((Strut)redDStruts.get(j)).applyConstraint();
      }      
      for (int j=0; j<greenDStruts.size();j++){
        ((Strut)greenDStruts.get(j)).applyConstraint();
      }      
      for (int j=0; j<blueDStruts.size();j++){
        ((Strut)blueDStruts.get(j)).applyConstraint();
      }      
      for (int k=0; k<otherParticle.size();k++){        
        Particle oParticle = (Particle)otherParticle.get(k);
        collidingWalls = mySpatialPartition.getWallsInRange(min(oParticle.position.x,oParticle.truePositionLast.x)-30,
        min(oParticle.position.y,oParticle.truePositionLast.y)-30,
        max(oParticle.position.x,oParticle.truePositionLast.x)+30,
        max(oParticle.position.y,oParticle.truePositionLast.y)+30);

        for (int m=0; m<collidingWalls.length; m++){
          collidingWalls[m].projectCollide( (Particle)otherParticle.get(k),collidePoint);
        }        
      }

    }
      playerCenter = new Vector3d(myPlayer.getParticle(0).position);
      collidingWalls = mySpatialPartition.getWallsInRange(playerCenter.x-100,playerCenter.y-100,playerCenter.x+100,playerCenter.y+100);
      for (int j=0; j<myPlayer.numParticles;j++){
        for (int k=0; k<collidingWalls.length; k++){
          collidingWalls[k].collideTest(myPlayer.getParticle(j),collidePoint);
        }
      }
    pushMatrix();
    translate(cameraX,cameraY);

    //draw sprites

    //draw player
    myPlayer.draw( ((i+1)/(1.0*stepsPerFrame))*255 );

    //draw FX particles
    if (i==0){
      drawFXParticles();
      stepFXParticles(tstep);
      noStroke();
    }

    popMatrix();

    stepCount++;
  }//end simulation loop

  pushMatrix();
  translate(cameraX,cameraY);
    if (!alreadyWonLevel){
      float rnum = 255*( 0.5 + .5*sin(frameNumber / 100.0+shootAngle) );
      float gnum = 255*( 0.5 + .5*sin(frameNumber / 100.0 + PI*2.0/3.0+shootAngle) );
      float bnum = 255*( 0.5 + .5*sin(frameNumber / 100.0 + PI*4.0/3.0+shootAngle) );
      for (int i=0; i<10; i++){
        fill(rnum*(10-i)/10.0,gnum*(10-i)/10.0,bnum*(10-i)/10.0,20);
        ellipse(goalLocation.x,goalLocation.y,5*(10-i),5*(10-i));
      }
    }
  for (int j=0; j<myStrut.size();j++){
    ((Strut)myStrut.get(j)).draw();
  } 
  for (int j=0; j<redDStruts.size();j++){
    ((Strut)redDStruts.get(j)).draw();
  }      
  for (int j=0; j<greenDStruts.size();j++){
    ((Strut)greenDStruts.get(j)).draw();
  }      
  for (int j=0; j<blueDStruts.size();j++){
    ((Strut)blueDStruts.get(j)).draw();
  }   
  for (int k=0; k<myWall.size(); k++){
    ((SolidWall2d)myWall.get(k)).draw();  
  }
  for (int j=0; j<otherParticle.size();j++){
    ((Particle)otherParticle.get(j)).draw();
  }

  noFill();
  if (debugFlag){ //debugging
    mySpatialPartition.draw();
  }
  popMatrix();  
  noStroke();
  if (debugFlag) println(debugCounter);
  frameNumber++;
}
